/*
** Copyright 2001/02, Thomas Kurschel. All rights reserved.
** Distributed under the terms of the NewOS License.
*/

//
// TODO:
// - "offsetof" macro is missing
//   -> move it to a public place

#include <kernel/module.h>
#include <kernel/lock.h>
#include <kernel/heap.h>
#include <kernel/vm.h>
#include <kernel/cpu.h>
#include <kernel/debug.h>
#include <kernel/khash.h>

#include <kernel/elf.h>

#include <string.h>

#define debug_level_flow -1
#define debug_level_error 0
#define debug_level_info 0

#define DEBUG_MSG_PREFIX "MODULE -- "

#include <kernel/debug_ext.h>

#if 0
#include <kernel/bus/isa/isa.h>
#endif



#ifndef offsetof
#define offsetof(type, member) ((size_t)&(((type *)0)->member))
#endif

bool modules_disable_user_addons = false;


typedef enum {
	module_loaded,
	module_initializing,
	module_ready,
	module_uninitializing,
	module_err_uninit
} module_state;

typedef struct module_info {
	struct module_info *next;
	struct module_image *image;
	module_header *header;
	int ref_count;
	bool keep_loaded;
	module_state state;
} module_info;


typedef struct module_image {
	struct module_image *next;
	int ref_count;
	bool keep_loaded;
	char *name;
	char *path;
	int base_path_id;
	module_header **headers;
} module_image;


// XXX locking scheme: there is a global lock only; having several locks
// makes trouble if dependent modules get loaded concurrently ->
// they have to wait for each other, i.e. we need one lock per module;
// also we must detect circular references during init and not dead-lock
static recursive_lock modules_lock;

const char *const module_paths[] = {
	"/boot/user-addons/modules",
	"/boot/addons/modules"
};

#define num_module_paths (sizeof( module_paths ) / sizeof( module_paths[0] ))

#define MODULES_HASH_SIZE 16

void *modules_images[num_module_paths];
void *modules_list;


static int module_info_compare( void *a, const void *key )
{
	module_info *module = a;
	const char *name = key;

	return strcmp( module->header->name, name );
}

static unsigned int module_info_hash( void *a, const void *key, unsigned int range )
{
	module_info *module = a;

	if( module != NULL )
		return hash_hash_str( module->header->name ) % range;
	else
		return hash_hash_str( key ) % range;
}

static int module_image_compare( void *a, const void *key )
{
	module_image *image = a;
	const char *name = key;

	return strcmp( image->name, name );
}

static unsigned int module_image_hash( void *a, const void *key, unsigned int range )
{
	module_image *image = a;

	if( image != NULL )
		return hash_hash_str( image->name ) % range;
	else
		return hash_hash_str( key ) % range;
}


static inline int check_module_header( module_header *header )
{
	if( header->version != MODULE_CURR_VERSION ) {
		SHOW_ERROR( 0, "module %s has unsupported version (%i)", header->name, header->version );
		return ERR_GENERAL;;
	}

	if( header->interface == NULL ) {
		SHOW_ERROR( 0, "module %s has no public interface", header->name );
		return ERR_GENERAL;
	}

	if( (header->flags & ~(MODULE_KEEP_LOADED)) != 0 ) {
		SHOW_ERROR( 0, "module %s has unknown flag(s) set (%x)", header->name, header->flags );
		return ERR_GENERAL;
	}

	return NO_ERROR;
}


static inline module_info *register_module( module_image *image, module_header *header )
{
	module_info *module;

	SHOW_FLOW( 3, "module %s in image %s", header->name, image->path );

	if( check_module_header( header ) != NO_ERROR )
		return NULL;

	module = (module_info *)kmalloc( sizeof( module_info ));

	if( module == NULL )
		return NULL;

	module->header = header;
	module->image = image;
	module->ref_count = 1;
	module->keep_loaded = header->flags & MODULE_KEEP_LOADED;
	image->keep_loaded |= module->keep_loaded;
	module->state = module_loaded;

	SHOW_FLOW0( 3, "adding to hash" );
	if( hash_insert( modules_list, module ) != NO_ERROR ) {
		kfree( module );
		return NULL;
	}

	++image->ref_count;

	SHOW_FLOW0( 3, "done" );

	return module;
}

static inline void unregister_module_image( module_image *image )
{
	SHOW_FLOW( 3, "name=%s", image->name );

	hash_remove( modules_images[image->base_path_id], image );

	kfree( image->name );
	kfree( image->path );
	kfree( image );
}

static void put_module_image( module_image *image )
{
	if( --image->ref_count == 0 ) {
		SHOW_FLOW( 3, "image %s not in use anymore", image->name );

		if( image->keep_loaded )
			return;

		SHOW_FLOW( 1, "unloading image... %s", image->path );
		elf_unload_kspace( image->path );
		SHOW_FLOW0( 1, "...done" );

		unregister_module_image( image );
	}
}

static inline void unregister_module( module_info *module )
{
	SHOW_FLOW( 3, "%s", module->header->name );
	hash_remove( modules_list, module );

	put_module_image( module->image );

	kfree( module );
}


static inline module_image *register_module_image_int( const char *image_name, const char *path,
	int base_path_id, module_header **headers, bool keep_loaded )
{
	module_image *image;

	image = (module_image *)kmalloc( sizeof( module_image ));

	if( image == NULL )
		return NULL;

	memset( image, 0, sizeof( image ));
	image->ref_count = 1;
	image->keep_loaded = keep_loaded;
	image->name = kstrdup( image_name );
	if( image->name == NULL )
		goto err;

	image->path = kstrdup( path );
	if( image->path == NULL )
		goto err2;

	image->base_path_id = base_path_id;
	image->headers = headers;

	SHOW_FLOW( 3, "adding image to hash %i", base_path_id );
	if( hash_insert( modules_images[base_path_id], image ) != NO_ERROR )
		goto err3;

	return image;

err3:
	kfree( image->path );
err2:
	kfree( image->name );
err:
	kfree( image );
	return NULL;
}

static module_image *register_module_image( const char *image_name, const char *path,
	int base_path_id, module_header **headers, bool keep_loaded )
{
	module_image *image;
	module_header **header;

	SHOW_FLOW( 3, "module %s", image_name );

	image = register_module_image_int( image_name, path, base_path_id, headers, keep_loaded );
	if( image == NULL )
		return NULL;

	for( header = image->headers; *header; ++header ) {
		if( strncmp( (*header)->name, image->name, strlen( image->name )) != 0 )
			SHOW_INFO( 0, "module %s stored in wrong image (%s)", (*header)->name, image->name );
	}

	return image;
}

static inline module_image *load_module_image( const char *image_name, const char *path, int base_path_id )
{
	image_id file_image;
	module_header **headers;
	module_image *image;

	SHOW_FLOW( 2, "loading %s", path );

	file_image = elf_load_kspace( path, "" );

	if( file_image < 0 ) {
		SHOW_FLOW( 3, "couldn't load image %s (%s)", path, strerror( file_image ));
		return NULL;
	}

	headers = (module_header **)elf_lookup_symbol( file_image, "modules" );

	if( headers == NULL ) {
		SHOW_INFO( 0, "image %s has no interface", path );
		elf_unload_kspace( path );
		return NULL;
	}

	SHOW_FLOW( 1, "loaded image %s", image_name );

	image = register_module_image( image_name, path, base_path_id, headers, false );

	if( image == NULL  ) {
		elf_unload_kspace( path );
		return NULL;
	}

	return image;
}

static inline module_header *find_module_in_image( module_image *image, const char *name )
{
	module_header **header;

	for( header = image->headers; *header; ++header ) {
		if( strcmp( (*header)->name, name ) == 0 ) {
			SHOW_FLOW( 3, "found module %s in image %s", name, image->name );
			return *header;
		}
	}

	SHOW_FLOW( 3, "cannot find module %s in image %s", name, image->name );
	return NULL;
}

static module_image *get_module_image( const char *name, size_t name_len, int base_path_id )
{
	char path[SYS_MAX_PATH_LEN];
	char *rel_name;
	size_t total_len;
	module_image *image;

	if( base_path_id == 0 && modules_disable_user_addons ) {
		SHOW_FLOW0( 3, "ignored - user add-ons are disabled" );
		return NULL;
	}

	total_len = min( name_len, SYS_MAX_PATH_LEN ) + 1;
	memcpy( path, name, total_len - 1 );
	path[total_len - 1] = 0;

	SHOW_FLOW( 3, "%s", path );

	image = hash_lookup( modules_images[base_path_id], (void *)path );

	if( image ) {
		SHOW_FLOW0( 3, "image already loaded" );
		++image->ref_count;
		return image;
	}

	total_len = strlen( module_paths[base_path_id] ) + 1 + name_len + 1;

	if( total_len > SYS_MAX_PATH_LEN ) {
		SHOW_FLOW0( 3, "ups, path too long" );
		return NULL;
	}

	strcpy( path, module_paths[base_path_id] );
	strcat( path, "/" );
	rel_name = path + strlen( path );

	strncat( path, name, name_len );
	path[total_len - 1] = 0;

	SHOW_FLOW( 3, "trying to load %s", path );

	image = load_module_image( rel_name, path, base_path_id );
	return image;
}

static module_info *search_module( const char *name )
{
	int pos;
	module_info *module;

	SHOW_FLOW( 3, "name: %s", name );

	module = hash_lookup( modules_list, (void *)name );

	if( module != NULL ) {
		SHOW_FLOW0( 3, "already loaded" );

		++module->ref_count;
		return module;
	}

	SHOW_FLOW0( 3, "try to load it" );
	pos = strlen( name );

	while( pos >= 0 ) {
		int i;

		for( i = 0; i < (int)num_module_paths; ++i ) {
			module_image *image;

			image = get_module_image( name, pos, i );

			if( image ) {
				module_header *header;

				SHOW_FLOW0( 3, "got image" );

				header = find_module_in_image( image, name );
				if( header ) {
					SHOW_FLOW0( 3, "got header" );

					module = register_module( image, header );

					put_module_image( image );
					return module;
				}

				put_module_image( image );
			}
		}

		--pos;

		while( pos > 0 && name[pos] != '/' )
			--pos;
	}

	return NULL;
}

static inline int init_module( module_info *module )
{
	int res;

	switch( module->state ) {
	case module_loaded:
		module->state = module_initializing;
		SHOW_FLOW( 3, "initing module %s... ", module->header->name );
		res = module->header->init();
		SHOW_FLOW( 3, "...done (%s)", strerror( res ));

		if( !res )
			module->state = module_ready;
		else
			module->state = module_loaded;
		break;

	case module_ready:
		res = NO_ERROR;
		break;

	case module_initializing:
		SHOW_ERROR( 0, "circular reference to %s", module->header->name );
		res = ERR_GENERAL;
		break;

	case module_uninitializing:
		SHOW_ERROR( 0, "tried to load module %s which is currently unloading", module->header->name );
		res = ERR_GENERAL;
		break;

	case module_err_uninit:
		SHOW_INFO( 0, "cannot load module %s because its earlier unloading failed", module->header->name );
		res = ERR_GENERAL;
		break;

	default:
		res = ERR_GENERAL;
	}

	return res;
}

static inline int uninit_module( module_info *module )
{
	switch( module->state ) {
	case module_loaded:
		return NO_ERROR;

	case module_initializing:
		panic( "Trying to unload module %s which is initializing",
			module->header->name );
		return ERR_GENERAL;

	case module_uninitializing:
		panic( "Trying to unload module %s which is un-initializing", module->header->name );
		return ERR_GENERAL;

	case module_ready:
		{
			int res;

			module->state = module_uninitializing;

			SHOW_FLOW( 2, "uniniting module %s...", module->header->name );
			res = module->header->uninit();
			SHOW_FLOW( 2, "...done (%s)", strerror( res ));

			if( res == NO_ERROR ) {
				module->state = module_loaded;
				return NO_ERROR;
			}

			SHOW_ERROR( 0, "Error unloading module %s (%i)", module->header->name, res );
		}

		module->state = module_err_uninit;
		module->keep_loaded = true;
		module->image->keep_loaded = true;

		// fall through
	default:
		return ERR_GENERAL;
	}
}

static int put_module_info( module_info *module )
{
	int res;

	if( --module->ref_count != 0 )
		return NO_ERROR;

	SHOW_FLOW( 2, "module %s not in use anymore", module->header->name );

	if( module->keep_loaded )
		return NO_ERROR;

	res = uninit_module( module );

	if( res == NO_ERROR )
		unregister_module( module );

	return res;
}

int module_get( const char *name, int flags, void **interface )
{
	module_info *module;
	int res;

	SHOW_FLOW( 0, "name=%s, flags=%i", name, flags );

	if( flags != 0 )
		return ERR_INVALID_ARGS;

	recursive_lock_lock( &modules_lock );

	module = search_module( name );

	if( !module ) {
		SHOW_FLOW( 3, "couldn't find module %s", name );
		res = ERR_NOT_FOUND;
		goto err;
	}

	SHOW_FLOW0( 3, "make sure module is ready" );
	res = init_module( module );

	if( res != NO_ERROR )
		goto err2;

	SHOW_FLOW0( 3, "module's ready to use" );

	*interface = module->header->interface;

	recursive_lock_unlock( &modules_lock );
	return res;

err2:
	put_module_info( module );
err:
	recursive_lock_unlock( &modules_lock );
	return res;
}

int module_put( const char *name )
{
	module_info *module;
	int res;

	SHOW_FLOW( 0, "name=%s", name );

	recursive_lock_lock( &modules_lock );

	module = hash_lookup( modules_list, (void *)name );
	if( module == NULL ) {
		res = ERR_NOT_FOUND;
		goto err;
	}

	res = put_module_info( module );

err:
	recursive_lock_unlock( &modules_lock );
	return res;
}


typedef struct module_iterator {
	char *prefix;
	module_image *cur_image;
	module_header **cur_header;
	int base_path_id;
	struct module_dir_iterator *base_dir, *cur_dir;
	int err;
	int prefix_pos;
	unsigned int prefix_base_path_id;
} module_iterator;

typedef struct module_dir_iterator {
	struct module_dir_iterator *parent_dir, *sub_dir;
	char *name;
	int file;
} module_dir_iterator;


modules_cookie module_open_list( const char *prefix )
{
	module_iterator *iter;

	SHOW_FLOW( 3, "prefix: %s", prefix );

	if( (iter = kmalloc( sizeof( *iter ))) == NULL )
		return NULL;

	iter->prefix = kstrdup( prefix );
	if( iter == NULL ) {
		kfree( iter );
		return NULL;
	}

	/*iter->cur_image = module_sys_image;
	++module_sys_image->ref_count;
	iter->cur_header = iter->cur_image->headers;*/
	iter->cur_image = NULL;
	iter->cur_header = NULL;

	iter->base_path_id = -1;
	iter->base_dir = iter->cur_dir = NULL;
	iter->err = NO_ERROR;
	iter->prefix_pos = strlen( prefix );

	iter->prefix_base_path_id = 0;

	return (modules_cookie)iter;
}


static inline int module_enter_image( module_iterator *iter, const char *image_name )
{
	module_image *image;

	SHOW_FLOW( 3, "%s", image_name );

	image = get_module_image( image_name, strlen( image_name ), iter->base_path_id );

	if( image == NULL ) {
		SHOW_FLOW( 3, "cannot get image %s", image_name );

		return NO_ERROR;
	}

	iter->cur_image = image;
	iter->cur_header = image->headers;

	SHOW_FLOW( 3, "entered image %s", image_name );
	return NO_ERROR;
}

static inline int module_create_dir_iterator( module_iterator *iter, int file, const char *name )
{
	module_dir_iterator *dir;

	dir = kmalloc( sizeof( *dir ));
	if( dir == NULL )
		return ERR_NO_MEMORY;

	dir->name = kstrdup( name );
	if( dir->name == NULL ) {
		kfree( dir );
		return ERR_NO_MEMORY;
	}

	dir->file = file;

	dir->sub_dir = NULL;
	dir->parent_dir = iter->cur_dir;

	if( iter->cur_dir )
		iter->cur_dir->sub_dir = dir;
	else
		iter->base_dir = dir;

	iter->cur_dir = dir;

	SHOW_FLOW( 3, "created dir iterator for %s", name );
	return NO_ERROR;
}

static inline int module_enter_dir( module_iterator *iter, const char *path, const char *name  )
{
	int file;
	int res;

	file = sys_opendir( path );
	if( file < 0 ) {
		SHOW_FLOW( 3, "couldn't open directory %s (%s)", path, strerror( file ));

		// there are so many errors for "not found" that we don't bother
		// and always assume that the directory suddenly disappeared
		return NO_ERROR;
	}

	res = module_create_dir_iterator( iter, file, name );
	if( res != NO_ERROR ) {
		sys_closedir( file );
		return ERR_NO_MEMORY;
	}

	SHOW_FLOW( 3, "entered directory %s", path );
	return NO_ERROR;
}


static inline void destroy_dir_iterator( module_iterator *iter )
{
	module_dir_iterator *dir;

	dir = iter->cur_dir;

	SHOW_FLOW( 3, "destroying directory iterator for sub-dir %s", dir->name );

	if( dir->parent_dir )
		dir->parent_dir->sub_dir = NULL;

	iter->cur_dir = dir->parent_dir;

	kfree( dir->name );
	kfree( dir );
}


static inline void module_leave_dir( module_iterator *iter )
{
	module_dir_iterator *parent_dir;

	SHOW_FLOW( 3, "leaving directory %s", iter->cur_dir->name );

	parent_dir = iter->cur_dir->parent_dir;

	sys_closedir( iter->cur_dir->file );
	destroy_dir_iterator( iter );

	iter->cur_dir = parent_dir;
}

static void compose_path( char *path, module_iterator *iter, const char *name, bool full_path )
{
	module_dir_iterator *dir;

	if( full_path ) {
		strlcpy( path, iter->base_dir->name, SYS_MAX_PATH_LEN );
		strlcat( path, "/", SYS_MAX_PATH_LEN );
	} else {
		strlcpy( path, iter->prefix, SYS_MAX_PATH_LEN );
		if( *iter->prefix )
			strlcat( path, "/", SYS_MAX_PATH_LEN );
	}

	for( dir = iter->base_dir->sub_dir; dir; dir = dir->sub_dir ) {
		strlcat( path, dir->name, SYS_MAX_PATH_LEN );
		strlcat( path, "/", SYS_MAX_PATH_LEN );
	}

	strlcat( path, name, SYS_MAX_PATH_LEN );

	SHOW_FLOW( 3, "name: %s, %s -> %s", name,
		full_path ? "full path" : "relative path",
		path );
}


static inline int module_traverse_dir( module_iterator *iter )
{
	int res;
	struct file_stat stat;
	char name[SYS_MAX_NAME_LEN];
	char path[SYS_MAX_PATH_LEN];

	SHOW_FLOW( 3, "scanning %s", iter->cur_dir->name );

	if( (res = sys_readdir( iter->cur_dir->file, name, sizeof( name ))) <= 0 ) {
		SHOW_FLOW( 3, "got error: %s", strerror( res ));
		module_leave_dir( iter );
		return NO_ERROR;
	}

	SHOW_FLOW( 3, "got %s", name );

	if( strcmp( name, "." ) == 0 ||
		strcmp( name, ".." ) == 0 )
		return NO_ERROR;

	// currently, sys_read returns an error if buffer is too small
	// I don't know the official specification, so it's always safe
	// to add a trailing end-of-string
	name[sizeof(name) - 1] = 0;

	compose_path( path, iter, name, true );

	if( (res = sys_rstat( path, &stat)) != NO_ERROR )
		return res;

	switch( stat.type ) {
		case STREAM_TYPE_FILE:
			compose_path( path, iter, name, false );

			return module_enter_image( iter, path );

		case STREAM_TYPE_DIR:
			return module_enter_dir( iter, path, name );

		default:
			SHOW_FLOW( 3, "entry %s not a file nor a directory - ignored", name );
			return NO_ERROR;
	}
}


static inline int module_enter_master_image( module_iterator *iter )
{
	module_image *image;

	SHOW_FLOW0( 3, "" );

	if( iter->prefix_base_path_id >= num_module_paths ) {

		SHOW_FLOW0( 3, "reducing image name" );
		--iter->prefix_pos;

		while(
			iter->prefix_pos > 0 &&
			iter->prefix[iter->prefix_pos] != '/' )
		{
			--iter->prefix_pos;
		}

		iter->prefix_base_path_id = 0;

		if( iter->prefix_pos < 0 ) {
			SHOW_FLOW0( 3, "no possible master image left" );
			return NO_ERROR;
		}
	}

	SHOW_FLOW0( 3, "trying new image" );
	image = get_module_image( iter->prefix, iter->prefix_pos, iter->prefix_base_path_id );

	++iter->prefix_base_path_id;

	if( image ) {
		SHOW_FLOW( 3, "got image %s", image->name );

		iter->cur_image = image;
		iter->cur_header = image->headers;
	}

	return NO_ERROR;
}

static inline int module_enter_base_path( module_iterator *iter )
{
	char path[SYS_MAX_PATH_LEN];
	int res;

	++iter->base_path_id;

	if( iter->base_path_id >= (int)num_module_paths ) {
		SHOW_FLOW0( 3, "no locations left" );
		return ERR_NOT_FOUND;
	}

	SHOW_FLOW( 3, "checking new base path (%s)", module_paths[iter->base_path_id] );

	if( iter->base_path_id == 0 && modules_disable_user_addons ) {
		SHOW_FLOW0( 3, "ignoring user add-ons (they are disabled)" );
		return NO_ERROR;
	}

	strcpy( path, module_paths[iter->base_path_id] );
	if( *iter->prefix ) {
		strcat( path, "/" );
		strlcat( path, iter->prefix, sizeof( path ));
	}

	res = module_enter_dir( iter, path, path );

	return res;
}

static inline bool iter_check_module_header( module_iterator *iter )
{
	module_info *module;
	module_image *tmp_image;
	const char *name;

	if( *iter->cur_header == NULL ) {
		SHOW_FLOW0( 3, "reached end of modules list in image" );

		put_module_image( iter->cur_image );

		iter->cur_header = NULL;
		iter->cur_image = NULL;

		return false;
	}

	name = (*iter->cur_header)->name;

	SHOW_FLOW( 3, "checking %s", name );

	if( strlen( name ) < strlen( iter->prefix ) ||
		memcmp( name, iter->prefix, strlen( iter->prefix )) != 0 )
	{
		SHOW_FLOW( 3, "module %s has wrong prefix", name );

		++iter->cur_header;
		return false;
	}

	module = search_module( name );

	if( module == NULL ) {
		SHOW_FLOW( 3, "couldn't get module %s -> must be broken", name );

		++iter->cur_header;
		return false;
	}

	SHOW_FLOW0( 3, "get rid of image" );

	tmp_image = module->image;
	++tmp_image->ref_count;

	put_module_info( module );

	if( iter->base_path_id >= 0 ) {
		if( strlen( tmp_image->name ) < strlen( iter->prefix ) ||
			tmp_image->base_path_id != iter->base_path_id )
		{
			if( strlen( tmp_image->name ) < strlen( iter->prefix ))
				SHOW_FLOW( 3, "image name (%s) is shorter then prefix -> already scanned",
					tmp_image->name );
			else
				SHOW_FLOW( 3, "image (%s) is from wrong base path", tmp_image->name );

			put_module_image( tmp_image );
			++iter->cur_header;
			return false;
		}
	}

	put_module_image( tmp_image );

	SHOW_FLOW( 3, "found module %s", name );
	return true;
}

int read_next_module_name( modules_cookie cookie, char *buf, size_t *bufsize )
{
	module_iterator *iter = (module_iterator *)cookie;
	int res;

	if( !iter )
		return ERR_INVALID_ARGS;

	res = iter->err;

	while( res == NO_ERROR ) {
		if( iter->cur_header ) {
			SHOW_FLOW0( 3, "having image to scan" );

			if( iter_check_module_header( iter )) {
				strlcpy( buf, (*iter->cur_header)->name, *bufsize );
				*bufsize = strlen( buf );

				++iter->cur_header;

				res = NO_ERROR;
				break;
			}
		} else {
			SHOW_FLOW0( 3, "looking for new image" );

			if( iter->cur_dir == NULL ) {
				if( iter->prefix_pos >= 0 )
					res = module_enter_master_image( iter );
				else
					res = module_enter_base_path( iter );
			} else
				res = module_traverse_dir( iter );
		}
	}

	iter->err = res;

	SHOW_FLOW( 3, "finished with status %s", strerror( iter->err ));
	return iter->err;
}


int close_module_list( modules_cookie cookie )
{
	module_iterator *iter = (module_iterator *)cookie;

	SHOW_FLOW0( 3, "" );

	if( !iter )
		return ERR_INVALID_ARGS;

	while( iter->cur_dir )
		module_leave_dir( iter );

	kfree( iter->prefix );
	kfree( iter );
	return NO_ERROR;
}

module_header *dummy[] = { NULL };


int module_init( kernel_args *ka, module_header **sys_module_headers )
{
	unsigned int i;

	SHOW_FLOW0( 0, "" );
	recursive_lock_create( &modules_lock );

	modules_list = hash_init( MODULES_HASH_SIZE,
		offsetof( module_info, next ),
		module_info_compare, module_info_hash );

	if( modules_list == NULL )
		return ERR_NO_MEMORY;

	for( i = 0; i < num_module_paths; ++i ) {
		modules_images[i] = hash_init( MODULES_HASH_SIZE,
			offsetof( module_image, next ),
			module_image_compare, module_image_hash);

		if( modules_images[i] == NULL )
			return ERR_NO_MEMORY;
	}

	if( sys_module_headers == NULL )
		sys_module_headers = dummy;

	if( register_module_image( "", "(built-in)", 0, sys_module_headers, true ) == NULL )
		return ERR_NO_MEMORY;

	// XXX: VERY VERY BAD LINK HACK!
	// we need to link all libc functions into kernel, but there are
	// some user space functions in libc that obviously must not be
	// added to the kernel image -> split libc in common and user space
	// only lib and force linking of entire common lib into kernel via
	// --whole-archive
	//
	// meanwhile, we make sure that nothing is missing by referencing all
	// functions manually (ouch!)

	#if 1
	{
		const char *dummy;

		dummy = strerror( NO_ERROR );
	}
	#endif

	#if 0
	{
		isa_bus_manager *isa_interface;
		int res;
		int i;
		void *iterator;
		char module_name[SYS_MAX_PATH_LEN];
		size_t name_len;
		const char prefix[] = "bus_managers";
		modules_cookie modules_cookie;

		if( (res = module_get( ISA_MODULE_NAME, 0, (void **)&isa_interface )) != NO_ERROR )
			dprintf( "Cannot load isa module (%s)", strerror( res ));
		else {
			for( i = 'A'; i <= 'Z'; ++i )
				isa_interface->write_io_8( 0xe9, i );

			module_put( ISA_MODULE_NAME );
		}

		dprintf( "Getting modules with prefix %s", prefix );
		modules_cookie = module_open_list( prefix );

		while( name_len = sizeof( module_name ),
			read_next_module_name( modules_cookie, module_name, &name_len ) == NO_ERROR )
		{
			dprintf( "Found module %s", module_name );
		}

		close_module_list( modules_cookie );
		dprintf( "done" );
	}
	#endif

	#if 0
	{
		void *interface;

		module_get( "busses/ide/ide_isa", 0, &interface );
	}
	#endif

	return NO_ERROR;
}
